#include "SerialPort.h"
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include <cstdint> // Explicitly include for uintX_t types
#include <vector>
#include <string>

#ifdef _WIN32
#include <initguid.h> // For GUID_DEVCLASS_PORTS
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
#include <tchar.h>
#pragma comment(lib, "Setupapi.lib")
#else
#include <fcntl.h>
#include <cerrno>
#include <dirent.h>
#include <cstring>
#endif

SerialPort::SerialPort() :
    portOpen(false),
    totalBytesSent(0),
    totalBytesReceived(0),
    receiving(false)
#ifdef _WIN32
    , hSerial(INVALID_HANDLE_VALUE)
#else
    , fd(-1)
#endif
{
}

SerialPort::SerialPort(SerialPort&& other) noexcept
    : portOpen(other.portOpen),
      totalBytesSent(other.totalBytesSent),
      totalBytesReceived(other.totalBytesReceived),
      receiving(other.receiving.load()),
      dataCallback(std::move(other.dataCallback)),
      receiveThread(std::move(other.receiveThread)),
      currentPortName(std::move(other.currentPortName)),
      lastErrorInfo(std::move(other.lastErrorInfo))
#ifdef _WIN32
    , hSerial(other.hSerial)
#else
    , fd(other.fd)
    , tty(other.tty)
#endif
{
    other.portOpen = false;
    other.receiving.store(false);
#ifdef _WIN32
    other.hSerial = INVALID_HANDLE_VALUE;
#else
    other.fd = -1;
#endif
}

SerialPort& SerialPort::operator=(SerialPort&& other) noexcept
{
    if (this == &other) {
        return *this;
    }
    disableReceive();
    closePort();

    portOpen = other.portOpen;
    totalBytesSent = other.totalBytesSent;
    totalBytesReceived = other.totalBytesReceived;
    receiving.store(other.receiving.load());
    dataCallback = std::move(other.dataCallback);
    receiveThread = std::move(other.receiveThread);
    currentPortName = std::move(other.currentPortName);
    lastErrorInfo = std::move(other.lastErrorInfo);
#ifdef _WIN32
    hSerial = other.hSerial;
#else
    fd = other.fd;
    tty = other.tty;
#endif
    other.portOpen = false;
    other.receiving.store(false);
#ifdef _WIN32
    other.hSerial = INVALID_HANDLE_VALUE;
#else
    other.fd = -1;
#endif
    return *this;
}

SerialPort::~SerialPort() {
    disableReceive();
    closePort();
}

#ifdef _WIN32
#else
static std::string readFile(const std::string& path) {
    std::ifstream file(path);
    if (!file.is_open()) {
        return "";
    }
    std::string content;
    std::getline(file, content);
    return content;
}
#endif

std::vector<SerialPortInfo> SerialPort::listAvailablePorts() {
    std::vector<SerialPortInfo> ports{};
#ifdef _WIN32
    HDEVINFO hDevInfo = SetupDiGetClassDevs(
        &GUID_DEVCLASS_PORTS,
        nullptr,
        nullptr,
        DIGCF_PRESENT | DIGCF_PROFILE
    );

    if (hDevInfo == INVALID_HANDLE_VALUE) {
        return ports;
    }

    SP_DEVINFO_DATA devInfoData;
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); ++i) {
        HKEY hDeviceKey = SetupDiOpenDevRegKey(
            hDevInfo,
            &devInfoData,
            DICS_FLAG_GLOBAL,
            0,
            DIREG_DEV,
            KEY_READ
        );

        if (hDeviceKey != INVALID_HANDLE_VALUE) {
            char portName[256];
            DWORD portNameSize = sizeof(portName);
            if (RegQueryValueExA(hDeviceKey, "PortName", nullptr, nullptr, (LPBYTE)portName, &portNameSize) == ERROR_SUCCESS) {
                SerialPortInfo portInfo;
                portInfo.port = portName;
                TCHAR devDesc[256];
                if (SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, (PBYTE)devDesc, sizeof(devDesc), nullptr)) {
                    portInfo.description = devDesc;
                } else {
                    // Fallback to device description if friendly name is not available
                    if (SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_DEVICEDESC, nullptr, (PBYTE)devDesc, sizeof(devDesc), nullptr)) {
                        portInfo.description = devDesc;
                    }
                }
                ports.push_back(portInfo);
            }
            RegCloseKey(hDeviceKey);
        }
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);

#else
    const char* tty_dir = "/sys/class/tty";
    DIR *dir;
    struct dirent *ent;

    if ((dir = opendir(tty_dir)) == nullptr) {
        // Cannot open /sys/class/tty, return empty list
        return ports;
    }

    while ((ent = readdir(dir)) != nullptr) {
        std::string device_name = ent->d_name;

        // Filter for common serial port patterns and ignore '.' and '..'
        if (device_name.rfind("ttyS", 0) == 0 ||
            device_name.rfind("ttyUSB", 0) == 0 ||
            device_name.rfind("ttyACM", 0) == 0)
        {
            SerialPortInfo portInfo;
            portInfo.port = "/dev/" + device_name;
            // --- Find device description ---
            std::string device_path = std::string(tty_dir) + "/" + device_name + "/device";
            // Use realpath to resolve symbolic links.
            char real_path_buffer[PATH_MAX];
            if (realpath(device_path.c_str(), real_path_buffer)) {
                std::string current_path = real_path_buffer;
                // Walk up the device tree to find manufacturer/product info
                while (current_path != "/" && current_path.rfind("/sys/", 0) == 0) {
                    std::string manufacturer = readFile(current_path + "/manufacturer");
                    std::string product = readFile(current_path + "/product");
                    if (!manufacturer.empty() && !product.empty()) {
                        portInfo.description = manufacturer + " " + product;
                        break; // Found it
                    }
                    // Go one level up
                    size_t last_slash = current_path.find_last_of('/');
                    if (last_slash == std::string::npos) {
                        break;
                    }
                    current_path = current_path.substr(0, last_slash);
                }
            }
            if (portInfo.description.empty()) {
                portInfo.description = "Serial Port"; // Fallback
            }
            ports.push_back(portInfo);
        }
    }
    closedir(dir);
#endif
    return ports;
}

bool SerialPort::setPortParameters(const PortParam& param)
{
    if (!portOpen) {
        lastErrorInfo = "Port is not open. Cannot set parameters.";
        return false;
    }

#ifdef _WIN32
    // Clear any pending data before setting parameters
    PurgeComm(hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);

    DCB serialParams = {0};
    serialParams.DCBlength = sizeof(serialParams);

    if (!GetCommState(hSerial, &serialParams)) {
        lastErrorInfo = "Error getting current serial port state. GetLastError: " + std::to_string(GetLastError());
        return false;
    }
    // 配置波特率
    serialParams.BaudRate = static_cast<unsigned long>(param.baudRate);
    // 配置数据位
    serialParams.ByteSize = static_cast<unsigned char>(param.dataBits);
    // 配置校验位
    serialParams.Parity = static_cast<unsigned char>(param.parity);
    // 配置停止位
    serialParams.StopBits = static_cast<unsigned char>(param.stopBits);
    // 应用配置
    if (!SetCommState(hSerial, &serialParams)) {
        lastErrorInfo = "Error setting serial port state.";
        return false;
    }

    // Set timeouts for non-blocking read
    COMMTIMEOUTS timeouts = {0};
    timeouts.ReadIntervalTimeout = MAXDWORD; // Return immediately if no bytes are available
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (!SetCommTimeouts(hSerial, &timeouts)) {
        lastErrorInfo = "Error setting serial port timeouts. GetLastError: " + std::to_string(GetLastError());
        return false;
    }

#else
    // Linux/Unix-like systems
    if (tcgetattr(fd, &tty) != 0) {
        lastErrorInfo = "Error from tcgetattr: " + std::string(strerror(errno));
        return false;
    }

    tty.c_cflag &= ~PARENB; // Clear parity bit
    tty.c_cflag &= ~CSTOPB; // Clear stop bit
    tty.c_cflag &= ~CSIZE;  // Clear all the size bits

    switch (param.dataBits) {
        case DataBit::DATABIT_5: tty.c_cflag |= CS5; break;
        case DataBit::DATABIT_6: tty.c_cflag |= CS6; break;
        case DataBit::DATABIT_7: tty.c_cflag |= CS7; break;
        case DataBit::DATABIT_8: tty.c_cflag |= CS8; break;
        default:
            lastErrorInfo = "Invalid data bits. Must be 5, 6, 7, or 8.";
            return false;
    }

    switch (param.parity) {
        case Parity::NONE: tty.c_cflag &= ~PARENB; break; // No parity
        case Parity::ODD: tty.c_cflag |= PARENB; tty.c_cflag |= PARODD; break; // Odd parity
        case Parity::EVEN: tty.c_cflag |= PARENB; tty.c_cflag &= ~PARODD; break; // Even parity
        default:
            lastErrorInfo = "Invalid parity. Must be N, O, or E.";
            return false;
    }

    switch (param.stopBits) {
        case StopBits::ONE: tty.c_cflag &= ~CSTOPB; break; // 1 stop bit
        case StopBits::TWO: tty.c_cflag |= CSTOPB; break; // 2 stop bits
        default:
            lastErrorInfo = "Invalid stop bits. Must be 1 or 2.";
            return false;
    }

    speed_t baud_const;
    switch (param.baudRate) {
        case Baudrate::BAUDRATE_0:        baud_const = B0; break;
        case Baudrate::BAUDRATE_50:        baud_const = B50; break;
        case Baudrate::BAUDRATE_75:        baud_const = B75; break;
        case Baudrate::BAUDRATE_110:        baud_const = B110; break;
        case Baudrate::BAUDRATE_134:        baud_const = B134; break;
        case Baudrate::BAUDRATE_150:        baud_const = B150; break;
        case Baudrate::BAUDRATE_200:        baud_const = B200; break;
        case Baudrate::BAUDRATE_300:        baud_const = B300; break;
        case Baudrate::BAUDRATE_600:        baud_const = B600; break;
        case Baudrate::BAUDRATE_1200:       baud_const = B1200; break;
        case Baudrate::BAUDRATE_1800:       baud_const = B1800; break;
        case Baudrate::BAUDRATE_2400:       baud_const = B2400; break;
        case Baudrate::BAUDRATE_4800:       baud_const = B4800; break;
        case Baudrate::BAUDRATE_9600:       baud_const = B9600; break;
        case Baudrate::BAUDRATE_19200:      baud_const = B19200; break;
        case Baudrate::BAUDRATE_38400:      baud_const = B38400; break;
        case Baudrate::BAUDRATE_57600:      baud_const = B57600; break;
        case Baudrate::BAUDRATE_115200:     baud_const = B115200; break;
        case Baudrate::BAUDRATE_230400:     baud_const = B230400; break;
        case Baudrate::BAUDRATE_460800:     baud_const = B460800; break;
        case Baudrate::BAUDRATE_921600:     baud_const = B921600; break;
        case Baudrate::BAUDRATE_1000000:    baud_const = B1000000; break;
        case Baudrate::BAUDRATE_1152000:    baud_const = B1152000; break;
        case Baudrate::BAUDRATE_2000000:    baud_const = B2000000; break;
        case Baudrate::BAUDRATE_2500000:    baud_const = B2500000; break;
        case Baudrate::BAUDRATE_3000000:    baud_const = B3000000; break;
        case Baudrate::BAUDRATE_3500000:    baud_const = B3500000; break;
        case Baudrate::BAUDRATE_4000000:    baud_const = B4000000; break;
        default:
            lastErrorInfo = "Invalid baud rate.";
            return false;
    }
    cfsetispeed(&tty, baud_const);
    cfsetospeed(&tty, baud_const);

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        lastErrorInfo = "Error from tcsetattr: " + std::string(strerror(errno));
        return false;
    }
#endif
    return true;
}

bool SerialPort::openPort(const PortParam& param) {
    if (portOpen) {
        lastErrorInfo = "Port is already open.";
        return false;
    }

#ifdef _WIN32
    hSerial = CreateFileA(
        (std::string("\\\\.\\") + param.portName).c_str(), // 不对后面的路径字符串进行任何常规解析，直接将它作为设备名传递给设备驱动层
        GENERIC_READ | GENERIC_WRITE,
        0,
        nullptr,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, // 当前设置为阻塞模式    阻塞：FILE_ATTRIBUTE_NORMAL  非阻塞：FILE_FLAG_OVERLAPPED
        nullptr
    );

    if (hSerial == INVALID_HANDLE_VALUE) {
        lastErrorInfo = "Error opening serial port: " + std::to_string(GetLastError());
        return false;
    }

    // Clear any pending data after opening the port
    PurgeComm(hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);

    portOpen = true;
    currentPortName = param.portName;
    totalBytesSent = 0;
    totalBytesReceived = 0;
    lastErrorInfo = "";

#else
    fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd == -1) {
        lastErrorInfo = "Error opening serial port: " + std::string(strerror(errno));
        return false;
    }

    // Configure port for non-blocking read
    struct termios tty_config{};
    if (tcgetattr(fd, &tty_config) != 0) {
        lastErrorInfo = "Error from tcgetattr: " + std::string(strerror(errno));
        close(fd);
        fd = -1;
        return false;
    }

    tty_config.c_cflag |= (CLOCAL | CREAD); // Enable receiver, local mode
    tty_config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Raw input
    tty_config.c_oflag &= ~OPOST; // Raw output
    tty_config.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
    tty_config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable special handling of bytes

    tty_config.c_cc[VMIN] = 0;  // Read doesn't block
    tty_config.c_cc[VTIME] = 5; // 0.5 seconds read timeout

    if (tcsetattr(fd, TCSANOW, &tty_config) != 0) {
        lastErrorInfo = "Error from tcsetattr: " + std::string(strerror(errno));
        close(fd);
        fd = -1;
        return false;
    }

    portOpen = true;
    currentPortName = portName;
    totalBytesSent = 0;
    totalBytesReceived = 0;
    lastErrorInfo = "";
#endif
    if (!setPortParameters(param))
    {
        return false;
    }
    return true;
}

void SerialPort::closePort() {
    if (!portOpen)
    {
        return;
    }
    disableReceive();

#ifdef _WIN32
    CloseHandle(hSerial);
    hSerial = INVALID_HANDLE_VALUE;
#else
    close(fd);
    fd = -1;
#endif
    portOpen = false;
    currentPortName = "";
}

bool SerialPort::enableReceive(DataCallback callback) {
    if (!portOpen) {
        lastErrorInfo = "Port is not open.";
        return false;
    }
    if (receiving.load()) {
        lastErrorInfo = "Receiving is already enabled.";
        return false; // 已经启动
    }
    if (!callback) {
        lastErrorInfo = "Callback function is not valid.";
        return false;
    }

    dataCallback = std::move(callback);
    receiving.store(true);
    // 启动线程，并将 receiveLoop 作为线程函数
    receiveThread = std::thread(&SerialPort::receiveLoop, this);
    return true;
}

void SerialPort::disableReceive() {
    if (!receiving.load()) {
        return;
    }

    receiving.store(false); // 发送停止信号
    if (receiveThread.joinable()) {
        receiveThread.join(); // 等待线程执行完毕
    }
}

void SerialPort::receiveLoop()
{
    const int TEMP_BUFFER_SIZE = 1024;
    uint8_t buffer[TEMP_BUFFER_SIZE];

    while (receiving.load())
    {
#ifdef _WIN32
        DWORD bytesRead = 0;
        if (!ReadFile(hSerial, buffer, TEMP_BUFFER_SIZE, &bytesRead, nullptr)) {
            // 发生真正的错误
            lastErrorInfo = "Error reading from serial port: " + std::to_string(GetLastError());
            receiving = false; // 停止循环
            break;
        }
        if (bytesRead > 0) {
            totalBytesReceived += bytesRead;
            if (dataCallback) {
                dataCallback(buffer, bytesRead);
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
#else
        // Linux: 使用 select 实现 I/O 多路复用
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(fd, &read_fds);

        struct timeval timeout{0,100000}; // 100ms

        int ret = select(fd + 1, &read_fds, nullptr, nullptr, &timeout);
        if (ret < 0) {
            // Select 错误
            lastErrorInfo = "select() failed: " + std::string(strerror(errno));
            receiving.store(false); // 停止循环
            break;
        }

        if (ret > 0 && FD_ISSET(fd, &read_fds)) {
            // 数据可读
            ssize_t bytesRead = read(fd, buffer, TEMP_BUFFER_SIZE);
            if (bytesRead > 0) {
                totalBytesReceived += bytesRead;
                if (dataCallback) {
                    dataCallback(buffer, bytesRead);
                }
            } else if (bytesRead < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
                // 读取错误
                lastErrorInfo = "read() failed: " + std::string(strerror(errno));
                receiving.store(false); // 停止循环
            }
        }
#endif
    }
}

int32_t SerialPort::serialReceive(uint8_t* buffer, int bufferSize) {
    if (!portOpen) {
        lastErrorInfo = "Port is not open. Cannot read data.";
        return -1;
    }

    int32_t bytesRead = 0;
#ifdef _WIN32
    DWORD dwBytesRead = 0;
    if (!ReadFile(hSerial, buffer, bufferSize, &dwBytesRead, nullptr)) {
        lastErrorInfo = "Error reading from serial port: " + std::to_string(GetLastError());
        return -1;
    }
    bytesRead = static_cast<int32_t>(dwBytesRead);
#else
    bytesRead = static_cast<int32_t>(read(fd, buffer, bufferSize));
    if (bytesRead == -1) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            lastErrorInfo = "Error reading from serial port: " + std::string(strerror(errno));
            return -1;
        }
        bytesRead = 0; // No data available, not an error
    }
#endif

    if (bytesRead > 0) {
        totalBytesReceived += bytesRead;
    }
    return bytesRead;
}

int32_t SerialPort::serialTransmit(const uint8_t* data, int32_t dataSize) {
    if (!portOpen) {
        lastErrorInfo = "Port is not open. Cannot write data.";
        return -1;
    }

    int32_t bytesWritten = 0;
#ifdef _WIN32
    DWORD dwBytesWritten = 0;
    if (!WriteFile(hSerial, data, dataSize, &dwBytesWritten, nullptr)) {
        lastErrorInfo = "Error writing to serial port: " + std::to_string(GetLastError());
        return -1;
    }
    bytesWritten = static_cast<int32_t>(dwBytesWritten);
#else
    bytesWritten = static_cast<int32_t>(write(fd, data, dataSize));
    if (bytesWritten == -1) {
        lastErrorInfo = "Error writing to serial port: " + std::string(strerror(errno));
        return -1;
    }
#endif

    if (bytesWritten > 0) {
        totalBytesSent += bytesWritten;
    }
    return bytesWritten;
}

void SerialPort::getStatus(uint64_t& bytesSent, uint64_t& bytesReceived, std::string& errorInfo) {
    bytesSent = totalBytesSent;
    bytesReceived = totalBytesReceived;
    errorInfo = lastErrorInfo;
    lastErrorInfo = ""; // Clear error after reading
}

bool SerialPort::isOpen() const {
    return portOpen;
}

#ifndef _WIN32
int32_t SerialPort::getFd() const
{
    return fd;
}
#endif
